<template>
  <div id="app">
    <button @click="addCone">锥体</button>
    <button @click="addCylinder">柱体</button>
    <button @click="startAddingLine">添加辅助线</button>
    <div ref="container" class="container" style="width: 95vw; height: 80vh"></div>
  </div>
</template>

<script setup>
import { onMounted, ref, reactive, watch } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { DragControls } from 'three/examples/jsm/controls/DragControls.js'
import { GUI } from 'dat.gui'

const container = ref(null)
let scene, camera, renderer, controls, gui, dragControls
let lineStart = null
let tempLine = null // Temporarily visualizes the line
let addingLine = false

const settings = reactive({
  cone: {
    height: 2,
    radius: 1,
    segments: 3,
    positionX: 0,
    positionY: 0,
    positionZ: 0
  },
  cylinder: {
    height: 2,
    radius: 1,
    segments: 4,
    positionX: 0,
    positionY: 0,
    positionZ: 0
  }
})

onMounted(() => {
  initThree()
  gui = new GUI()
  document.addEventListener('mousedown', handleMouseDown)
  document.addEventListener('mousemove', handleMouseMove)
})

function initThree() {
  // const width = window.innerWidth
  // const height = window.innerHeight
  const width = container.value.clientWidth
  const height = container.value.clientHeight
  scene = new THREE.Scene()
  camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)
  camera.position.z = 5

  renderer = new THREE.WebGLRenderer({ antialias: true })
  renderer.setSize(width, height)
  renderer.setClearColor(new THREE.Color('#aaaaaa'))
  container.value.appendChild(renderer.domElement)

  controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true
  controls.dampingFactor = 0.05

  const axesHelper = new THREE.AxesHelper(5)
  axesHelper.position.y = -1
  scene.add(axesHelper)

  const gridHelper = new THREE.GridHelper(10, 10)
  gridHelper.position.y = -1
  scene.add(gridHelper)

  const objects = [] // 用于存储可拖拽的物体
  dragControls = new DragControls(objects, camera, renderer.domElement)

  // 添加事件监听，当拖拽开始和结束时禁用/启用轨道控制器
  dragControls.addEventListener('dragstart', function (event) {
    controls.enabled = false
  })
  dragControls.addEventListener('dragend', function (event) {
    controls.enabled = true
  })
  animate()
}

function animate() {
  requestAnimationFrame(animate)
  controls.update()
  renderer.render(scene, camera)
}

function addEdges(geometry, type) {
  const edges = new THREE.EdgesGeometry(geometry)
  const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0xffffff }))
  line.userData.type = type // 正确设置 userData.type
  scene.add(line)
  updateDraggableObjects()
  return line
}

function addCone() {
  const oldCone = scene.children.find((obj) => obj.userData.type === 'cone')
  if (oldCone) {
    scene.remove(oldCone)
  }
  const geometry = new THREE.ConeGeometry(
    settings.cone.radius,
    settings.cone.height,
    settings.cone.segments
  )
  const line = addEdges(geometry, 'cone')
  line.position.set(settings.cone.positionX, settings.cone.positionY, settings.cone.positionZ)

  if (gui && !guiPanels.cone) {
    addConeGUI()
    guiPanels.cone = true // 标记控制面板已创建
  }
}

function addCylinder() {
  const oldCylinder = scene.children.find((obj) => obj.userData.type === 'cylinder')
  if (oldCylinder) {
    scene.remove(oldCylinder)
  }

  const geometry = new THREE.CylinderGeometry(
    settings.cylinder.radius,
    settings.cylinder.radius,
    settings.cylinder.height,
    settings.cylinder.segments
  )
  const line = addEdges(geometry, 'cylinder')
  line.position.set(
    settings.cylinder.positionX,
    settings.cylinder.positionY,
    settings.cylinder.positionZ
  )

  if (gui && !guiPanels.cylinder) {
    addCylinderGUI()
    guiPanels.cylinder = true // 标记控制面板已创建
  }
}

//创建辅助线
function startAddingLine() {
  addingLine = true
  container.value.style.cursor = 'crosshair'
}

function handleMouseDown(event) {
  if (!addingLine) return

  const mouse = getMousePosition(event)
  const point = getPointOnPlane(mouse)

  if (!lineStart) {
    lineStart = point
    tempLine = createTempLine(lineStart, lineStart) // Start and end at the same point initially
    scene.add(tempLine)
  } else {
    finalizeLine(point)
    addingLine = false // Disable line adding mode
    container.value.style.cursor = 'default' // Reset cursor
  }
}

function handleMouseMove(event) {
  if (!addingLine || !lineStart) return

  const mouse = getMousePosition(event)
  const point = getPointOnPlane(mouse)
  updateTempLine(point)
}

function getMousePosition(event) {
  const rect = container.value.getBoundingClientRect()
  return new THREE.Vector2(
    ((event.clientX - rect.left) / rect.width) * 2 - 1,
    -((event.clientY - rect.top) / rect.height) * 2 + 1
  )
}

function getPointOnPlane(mouse) {
  const raycaster = new THREE.Raycaster()
  raycaster.setFromCamera(mouse, camera)
  const plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0)
  const target = new THREE.Vector3()
  raycaster.ray.intersectPlane(plane, target)
  return target
}

function createTempLine(start, end) {
  const material = new THREE.LineBasicMaterial({ color: 0xff0000 })
  const geometry = new THREE.BufferGeometry().setFromPoints([start, end])
  return new THREE.Line(geometry, material)
}

function updateTempLine(end) {
  tempLine.geometry.setFromPoints([lineStart, end])
  tempLine.geometry.verticesNeedUpdate = true
}

function finalizeLine(end) {
  updateTempLine(end) // Ensure the line is updated to the final point before finalizing
  lineStart = null // Reset the start point for future operations
}

//更新拖拽控制
function updateDraggableObjects() {
  const draggableObjects = scene.children.filter(
    (obj) => obj.userData.type === 'cone' || obj.userData.type === 'cylinder'
  )
  dragControls.transformGroup = true // 如果你想要整个组一起拖拽
  dragControls.objects = draggableObjects
}

//gui
const guiPanels = {
  cone: false,
  cylinder: false
}

function addConeGUI() {
  if (!gui) return
  const coneFolder = gui.addFolder('Cone Settings')
  coneFolder.add(settings.cone, 'height', 1, 5).onChange(addCone)
  coneFolder.add(settings.cone, 'radius', 0.5, 2).onChange(addCone)
  coneFolder.add(settings.cone, 'segments', 3, 32, 1).onChange(addCone)
  coneFolder.add(settings.cone, 'positionX', -5, 5, 0.1).onChange(addCone)
  coneFolder.add(settings.cone, 'positionY', -5, 5, 0.1).onChange(addCone)
  coneFolder.add(settings.cone, 'positionZ', -5, 5, 0.1).onChange(addCone)
  coneFolder.open()
}

function addCylinderGUI() {
  if (!gui) return
  const cylinderFolder = gui.addFolder('Cylinder Settings')
  cylinderFolder.add(settings.cylinder, 'height', 1, 5).onChange(addCylinder)
  cylinderFolder.add(settings.cylinder, 'radius', 0.5, 2).onChange(addCylinder)
  cylinderFolder.add(settings.cylinder, 'segments', 3, 32, 1).onChange(addCylinder)
  cylinderFolder.add(settings.cylinder, 'positionX', -5, 5, 0.1).onChange(addCylinder)
  cylinderFolder.add(settings.cylinder, 'positionY', -5, 5, 0.1).onChange(addCylinder)
  cylinderFolder.add(settings.cylinder, 'positionZ', -5, 5, 0.1).onChange(addCylinder)
  cylinderFolder.open()
}
</script>

<style></style>
